import type { IDeserializable } from "~/models/serialize/IDeserializable"
import { Deserializer } from "~/models/serialize/Deserializer"
import { shell } from "~/models/Logger"
import { PSlotType } from "./type"
import { Dzer } from "~/models/serialize"
import type { DataType } from "~/models/types/type"
import type { Value } from "~/models/value/Value"

export class PSlot implements IDeserializable {
  className: string = 'PSlot'

  static {
  }

  /**
   * 
   * @param name 槽的内在名，用于唯一性的确定节点上的槽。不展示
   * @param title 槽的展示名。注意，多数流程槽没有展示名(title)。只有内在名(name)
   * @param direction 槽的方向(进或出)
   * @param type 槽的类型(过程、函数等)：影响其在编译过程中的处理
   * @param dataType if [type === Data] 数据槽中，数据的类型
   * @param defaultValue if [type === Data] 数据槽中，数据的缺省值
   */
  constructor(
    public name: string = '',
    public title: string = '',

    // 槽的方向
    public direction: ('in' | 'out') = 'in',

    // 槽的连接类型
    public type: PSlotType = PSlotType.Process,

    /** 下面是仅数据类型槽的属性 */
    public dataType?: DataType,
    public defaultValue?: Value,
  ) {}

  same(that: PSlot): boolean {
    return (
      this.name === that.name
      && this.title === that.title
      && this.direction === that.direction
      && this.type === that.type
      && (this.dataType ? (this.dataType.is(that.dataType)) : true)
      && this.defaultValue?.content === that.defaultValue?.content
    )
  }

  /** 可否在图上替换成另一个槽 */
  replaceable(that: PSlot): boolean {
    return (
      this.direction === that.direction
      && this.type === that.type
      && (this.dataType ? (this.dataType.is(that.dataType)) : true)
    )
  }

  static readonly Preset = {
    ProcessIn: new PSlot('_in', '', 'in', PSlotType.Process),
    ProcessOut: new PSlot('_out', '', 'out', PSlotType.Process),
  }

  static InData(
    name: string,
    title: string,
    dataType?: DataType,
    defaultValue?: Value,
  ) {
    return new PSlot(name, title, 'in', PSlotType.Data, dataType, defaultValue)
  }

  static OutData(
    name: string,
    title: string,
    dataType?: DataType,
    defaultValue?: Value,
  ) {
    return new PSlot(name, title, 'out', PSlotType.Data, dataType, defaultValue)
  }

  public createMirror(): PSlot {
    const slot = Dzer.clone(this)
    slot.direction = slot.direction === 'in' ? 'out' : 'in'
    return slot
  }

  public compatibleWith(another?: PSlot, log = false): boolean {
    // 不连空，不自连
    if (!another || another === this) {
      return false
    }

    if (
      this.type !== another.type
      || this.direction === another.direction
    ) {
      return false
    }
    if (this.type === PSlotType.Process) {
      return true
    }
    else {
      const logWhenFailed = (success: boolean, message: string): boolean => {
        if (!success && log) {
          shell.warn(message)
        }
        return success
      }
      if (this.dataType === undefined || another.dataType === undefined) {
        
        console.log(this)
        console.log(another)
        throw '?????????????????????'
      }
      if (this.direction === 'in') {
        return logWhenFailed(
          this.dataType!.receiveType(another.dataType!),
          `类型(${this.dataType?.name})不接受类型(${another?.dataType?.name})`
        )
      }
      else {
        return logWhenFailed(
          another.dataType!.receiveType(this.dataType!),
          `类型(${another?.dataType?.name})不接受类型(${this.dataType?.name})`
        )
      }
      
    }
    
  }
}